#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <mutex>
#include <unistd.h>
#include "LockGuard.hpp"

using namespace std;

const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return bq_.size() == 0;
    }
    bool isQueueFull()
    {
        return bq_.size() == capacity_;
    }

public:
    BlockQueue(int capacity = gDefaultCap) : capacity_(capacity)
    {
        pthread_mutex_init(&mtx_, nullptr);
        pthread_cond_init(&Empty_, nullptr);
        pthread_cond_init(&Full_, nullptr);
    }
    void push(const T &in) // 生产者
    {
        // pthread_mutex_lock(&mtx_);
        // // 1. 先检测当前的临界资源是否能满足访问条件
        // // pthread_cond_wait：我们竟然是在临界区中！我是持有锁的！如果我去等待了，这把锁怎么办呢？
        // // pthread_cond_wait第二个参数是一把锁，当成功调用wait之后，传入的锁会被自动释放！
        // // 如果我被唤醒时，我从哪里醒来呢？？ 从哪里阻塞挂起，就从哪里唤醒，被唤醒的时候，我们还是在临界区被唤醒的
        // // 当我们被唤醒的时候，pthread_cond_wait，会自动帮助我们线程获取锁
        // // pthread_cond_wait: 只要是一个函数，就有可能失败
        // // pthread_cond_wait: 可能存在 伪唤醒 的情况，所以要用while循环判断
        // while (isQueueFull())
        //     pthread_cond_wait(&Full_, &mtx_);
        // // 2. 访问临界资源，100%确定，资源就是就绪的！
        // bq_.push(in);
        // // if (bq_.size() >= capacity_ / 2)
        //     pthread_cond_signal(&Empty_);
        // pthread_mutex_unlock(&mtx_);
        LockGuard lockguard(&mtx_);
        
        while (isQueueFull())
            pthread_cond_wait(&Full_, &mtx_);
        // 2. 访问临界资源，100%确定，资源就是就绪的！
        bq_.push(in);
        pthread_cond_signal(&Empty_);
    } // 自动调用 LockGuard 析构函数解锁
    void pop(T *out)
    {
        // pthread_mutex_lock(&mtx_);
        LockGuard lockguard(&mtx_);
        while (isQueueEmpty())
            pthread_cond_wait(&Empty_, &mtx_);
        *out = bq_.front();
        bq_.pop();
        pthread_cond_signal(&Full_);
        // pthread_mutex_unlock(&mtx_);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&Empty_);
        pthread_cond_destroy(&Full_);
    }

private:
    queue<T> bq_;          // 阻塞队列
    int capacity_;         // 容量上限
    pthread_mutex_t mtx_;  // 通过互斥锁保证队列安全
    pthread_cond_t Empty_; // 用它来表示 bq 是否为空的条件
    pthread_cond_t Full_;  // 用它来表示 bq 是否为满的条件
};